﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;

namespace GenericLibrary.Xml {

    public interface IXmlNodeBuilder {

        XmlNodeType NodeType { set; }
        string LocalName { set; }
        string Prefix { set; }
        string NamespaceUri { set; }
        object Value { set; }
        void CreateAttribute( string localName, string prefix, string namespaceUri, object value );
        void CreateAttribute( string localName, object value );
        void AddChildObject( object child );
    }

    public class XmlObjectReader : XmlReader, IXmlNodeBuilder {

        class XmlQName : IComparable<XmlQName> {

            public string LocalName, Prefix, NamespaceUri;

            public XmlQName( string localName, string prefix, string namespaceUri ) {
                this.LocalName = localName;
                this.Prefix = prefix;
                this.NamespaceUri = namespaceUri;
            }

            #region IComparable<XmlQName> Members

            public int CompareTo( XmlQName other ) {
                try {
                    if( object.ReferenceEquals( this, other ) ) {
                        return 0;
                    } else if( this.LocalName != other.LocalName ) {
                        return string.Compare( this.LocalName, other.LocalName );
                    } else if( this.Prefix != other.Prefix ) {
                        return string.Compare( this.Prefix, other.Prefix );
                    } else if( this.NamespaceUri != other.NamespaceUri ) {
                        return string.Compare( this.NamespaceUri, other.NamespaceUri );
                    } else {
                        return 0;
                    }
                } catch( Exception ) {
                    Debug.Assert( false );
                    throw;
                }
            }

            #endregion
        }

        public interface IObjectParser {

            bool Parse( object obj, IXmlNodeBuilder builder );
        }

        private IObjectParser _parser;
        private XmlNameTable _nameTable;
        private XmlNodeType _nodeType;
        private XmlQName _qName;
        private string _baseUri, _value;
        private SortedList<XmlQName, string> _attributes;
        private List<object> _objects;
        private Stack<int> _depthStack;
        private int _attributeIndex;
        private ReadState _readState;

        private void Initialize( string baseUri, IObjectParser parser, object obj ) {
            try {
                this._baseUri = baseUri;
                this._parser = parser;
                this._nameTable = new NameTable();
                this._nodeType = XmlNodeType.None;
                this._qName = new XmlQName( null, null, null );
                this._value = null;
                this._attributes = new SortedList<XmlQName, string>();
                this._objects = new List<object>();
                this._objects.Add( obj );
                this._depthStack = new Stack<int>();
                this._depthStack.Push( 1 );
                this._attributeIndex = -1;
                this._readState = ReadState.Initial;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XmlObjectReader( string baseUri, IObjectParser parser, object obj ) {
            try {
                this.Initialize( baseUri, parser, obj );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XmlObjectReader( IObjectParser parser, object obj ) {
            try {
                this.Initialize( null, parser, obj );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public override int AttributeCount {
            get { throw new NotImplementedException(); }
        }

        public override string BaseURI {
            get {
                return this._baseUri;
            }
        }

        public override void Close() {
            try {
                this._baseUri = null;
                this._parser = null;
                this._nameTable = null;
                this._nodeType = XmlNodeType.None;
                this._qName = null;
                this._value = null;
                this._attributes.Clear();
                this._attributes = null;
                this._objects.Clear();
                this._objects = null;
                this._depthStack.Clear();
                this._depthStack = null;
                this._attributeIndex = -1;
                this._readState = ReadState.Closed;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public override int Depth {
            get {
                try {
                    return this._depthStack.Count - 1;
                } catch( Exception ) {
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override bool EOF {
            get {
                try {
                    return this._readState == ReadState.EndOfFile;
                } catch( Exception ) {
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override string GetAttribute( int i ) {
            throw new NotImplementedException();
        }

        public override string GetAttribute( string name, string namespaceURI ) {
            throw new NotImplementedException();
        }

        public override string GetAttribute( string name ) {
            throw new NotImplementedException();
        }

        public override bool HasValue {
            get { throw new NotImplementedException(); }
        }

        public override bool IsEmptyElement {
            get {
                return false;
            }
        }

        public override string LocalName {
            get {
                try {
                    switch( this.NodeType ) {
                        case XmlNodeType.Attribute:
                            return this._attributes.Keys[ this._attributeIndex ].LocalName;
                        default:
                            return this._qName.LocalName;
                    }
                } catch( Exception ) {
                    this._readState = ReadState.Error;
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override string LookupNamespace( string prefix ) {
            throw new NotImplementedException();
        }

        public override bool MoveToAttribute( string name, string ns ) {
            throw new NotImplementedException();
        }

        public override bool MoveToAttribute( string name ) {
            throw new NotImplementedException();
        }

        public override bool MoveToElement() {
            throw new NotImplementedException();
        }

        public override bool MoveToFirstAttribute() {
            throw new NotImplementedException();
        }

        public override bool MoveToNextAttribute() {
            try {
                if( this._attributeIndex + 1 < this._attributes.Count ) {
                    this._attributeIndex++;
                    return true;
                }
                return false;
            } catch( Exception ) {
                this._readState = ReadState.Error;
                Debug.Assert( false );
                throw;
            }
        }

        public override XmlNameTable NameTable {
            get {
                try {
                    return this._nameTable;
                } catch( Exception ) {
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override string NamespaceURI {
            get {
                try {
                    switch( this.NodeType ) {
                        case XmlNodeType.Attribute:
                            return this._attributes.Keys[ this._attributeIndex ].NamespaceUri;
                        default:
                            return this._qName.NamespaceUri;
                    }
                } catch( Exception ) {
                    this._readState = ReadState.Error;
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override XmlNodeType NodeType {
            get {
                try {
                    if( this._attributeIndex >= 0 ) {
                        return XmlNodeType.Attribute;
                    }
                    return this._nodeType;
                } catch( Exception ) {
                    this._readState = ReadState.Error;
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override string Prefix {
            get {
                try {
                    switch( this.NodeType ) {
                        case XmlNodeType.Attribute:
                            return this._attributes.Keys[ this._attributeIndex ].Prefix;
                        default:
                            return this._qName.Prefix;
                    }
                } catch( Exception ) {
                    this._readState = ReadState.Error;
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        public override bool Read() {
            try {
                this._nodeType = XmlNodeType.None;
                this._qName.LocalName = null;
                this._qName.Prefix = null;
                this._qName.NamespaceUri = null;
                this._value = null;
                this._attributes.Clear();
                this._attributeIndex = -1;

                while( this._depthStack.Count > 0 && this._depthStack.Peek() == 0 ) {
                    this._depthStack.Pop();
                    if( this._depthStack.Count == 0 ) {
                        return false;
                    }
                }
                this._depthStack.Push( this._depthStack.Pop() - 1 );
                this._depthStack.Push( 0 );

                object obj = this._objects[ this._objects.Count - 1 ];
                this._objects.RemoveAt( this._objects.Count - 1 );

                if( this._parser.Parse( obj, this ) ) {
                    if( this._depthStack.Peek() > 0 ) {
                        this._objects.Reverse( this._objects.Count - this._depthStack.Peek(), this._depthStack.Peek() );
                    }
                    this._readState = ReadState.Interactive;
                    return true;
                } else {
                    this._readState = ReadState.EndOfFile;
                    return false;
                }
            } catch( Exception ) {
                this._readState = ReadState.Error;
                Debug.Assert( false );
                throw;
            }
        }

        public override bool ReadAttributeValue() {
            throw new NotImplementedException();
        }

        public override ReadState ReadState {
            get {
                return this._readState;
            }
        }

        public override void ResolveEntity() {
            throw new NotImplementedException();
        }

        public override string Value {
            get {
                try {
                    switch( this.NodeType ) {
                        case XmlNodeType.Attribute:
                            return this._attributes.Values[ this._attributeIndex ];
                        default:
                            return this._value;
                    }
                } catch( Exception ) {
                    this._readState = ReadState.Error;
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        #region IXmlNodeBuilder Members

        XmlNodeType IXmlNodeBuilder.NodeType {
            set {
                this._nodeType = value;
            }
        }

        string IXmlNodeBuilder.LocalName {
            set {
                this._qName.LocalName = this._nameTable.Add( value );
            }
        }

        string IXmlNodeBuilder.Prefix {
            set {
                this._qName.Prefix = this._nameTable.Add( value );
            }
        }

        string IXmlNodeBuilder.NamespaceUri {
            set {
                this._qName.NamespaceUri = this._nameTable.Add( value );
            }
        }

        object IXmlNodeBuilder.Value {
            set {
                try {
                    this._value = Convert.ToString( value );
                } catch( Exception ) {
                    Debug.Assert( false );
                    throw;
                }
            }
        }

        void IXmlNodeBuilder.CreateAttribute( string localName, string prefix, string namespaceUri, object value ) {
            try {
                this._attributes[ new XmlQName( this._nameTable.Add( localName ), this._nameTable.Add( prefix ), this._nameTable.Add( namespaceUri ) ) ] = Convert.ToString( value );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        void IXmlNodeBuilder.CreateAttribute( string localName, object value ) {
            ( this as IXmlNodeBuilder ).CreateAttribute( localName, null, null, value );
        }

        void IXmlNodeBuilder.AddChildObject( object child ) {
            try {
                if( child != null ) {
                    this._objects.Add( child );
                    this._depthStack.Push( this._depthStack.Pop() + 1 );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        #endregion






    }
}
